﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;

namespace 修为尽失.辅助.exts
{
    public static class Common
    {
        public static ConcurrentDictionary<Enum, string> _CacheDescriptions = new ConcurrentDictionary<Enum, string>();

        public static string AllMessage(this Exception ex)
        {
            if (ex == null)
                return string.Empty;
            var stringBuilder = new StringBuilder();
            stringBuilder.Append(ex.ToLogString());
            if (ex.InnerException != null)
            {
                stringBuilder.Append(Environment.NewLine);
                stringBuilder.Append(">").Append(ex.InnerException.ToLogString());
            }

            return stringBuilder.ToString();
        }
        /// <summary>
        /// 反射实现两个类的对象之间相同属性的值的复制
        /// 适用于初始化新实体
        /// </summary>
        /// <typeparam name="D">返回的实体</typeparam>
        /// <typeparam name="S">数据源实体</typeparam>
        /// <param name="s">数据源实体</param>
        /// <returns>返回的新实体</returns>
        public static D Mapper<D, S>(this S s)
        {
            D d = Activator.CreateInstance<D>(); //构造新实例
            try
            {
                var Types = s.GetType();//获得类型  
                var Typed = typeof(D);
                foreach (PropertyInfo sp in Types.GetProperties())//获得类型的属性字段  
                {
                    foreach (PropertyInfo dp in Typed.GetProperties())
                    {
                        if (dp.Name == sp.Name && dp.PropertyType == sp.PropertyType && dp.Name!= "Error" && dp.Name != "Item")//判断属性名是否相同  
                        {
                            dp.SetValue(d, sp.GetValue(s, null), null);//获得s对象属性的值复制给d对象的属性  
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return d;
        }
        /// <summary>
        /// 反射实现两个类的对象之间相同属性的值的复制
        /// 适用于没有新建实体之间
        /// </summary>
        /// <typeparam name="D">返回的实体</typeparam>
        /// <typeparam name="S">数据源实体</typeparam>
        /// <param name="d">返回的实体</param>
        /// <param name="s">数据源实体</param>
        /// <returns></returns>
        public static D MapperToModel<D, S>( D d,S s)
        {
            try
            {
                var Types = s.GetType();//获得类型  
                var Typed = typeof(D);
                foreach (PropertyInfo sp in Types.GetProperties())//获得类型的属性字段  
                {
                    foreach (PropertyInfo dp in Typed.GetProperties())
                    {
                        if (dp.Name == sp.Name && dp.PropertyType == sp.PropertyType && dp.Name != "Error" && dp.Name != "Item")//判断属性名是否相同  
                        {
                            dp.SetValue(d, sp.GetValue(s, null), null);//获得s对象属性的值复制给d对象的属性  
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return d;
        }
        public static T CopyT<T>(this T obj) where T : class
        {
            using (var memoryStream = new MemoryStream())
            {
                var binaryFormatter = new BinaryFormatter((ISurrogateSelector) null, new StreamingContext(StreamingContextStates.Clone));
                binaryFormatter.Serialize((Stream) memoryStream, (object) obj);
                memoryStream.Seek(0L, SeekOrigin.Begin);
                var obj1 = default(T);
                try
                {
                    obj1 = (T) binaryFormatter.Deserialize((Stream) memoryStream);
                }
                catch (Exception ex)
                {
                    throw;
                }

                return obj1;
            }
        }

        public static object CreateInstance(this Type @this)
        {
            try
            {
                return Activator.CreateInstance(@this);
            }
            catch
            {
                return (object) null;
            }
        }

        public static Type GetNullableGenericType(this Type type) => type.IsNullableType() ? type.GetGenericArguments()[0] : type;
        public static string GetPropertyName<T>(this System.Linq.Expressions.Expression<Func<T, object>> expr) => ((System.Linq.Expressions.MemberExpression) expr.Body).Member.Name;

        public static object GetValue(this PropertyInfo pi, object obj)
        {
            if ((pi == (PropertyInfo) null) || (obj == null))
                return (object) null;
            var getMethod = pi.GetGetMethod();
            if (getMethod == (MethodInfo) null)
                return (object) null;
            return getMethod.Invoke(obj, Constants.EmptyObjectArray);
        }

        public static bool IsBetween<T>(this T @this, T lower, T upper, bool includeLower = true, bool includeUpper = true) where T : IComparable<T>
        {
            if ((object) @this == null)
                return false;
            var num1 = @this.CompareTo(lower);
            var num2 = @this.CompareTo(upper);
            return (includeLower && (num1 == 0)) || (includeUpper && (num2 == 0)) || ((num1 > 0) && (num2 < 0));
        }

        public static bool IsBetween<T>(this T @this, T lower, T upper, IComparer<T> comparer, bool includeLower = true, bool includeUpper = true)
        {
            if ((object) @this == null)
                return false;
            var num1 = comparer.Compare(@this, lower);
            var num2 = comparer.Compare(@this, upper);
            return (includeLower && (num1 == 0)) || (includeUpper && (num2 == 0)) || ((num1 > 0) && (num2 < 0));
        }

        public static bool IsCompatibleType(this Type type1, Type type2)
        {
            if ((type1 == type2) || (type1.IsEnum && (Enum.GetUnderlyingType(type1) == type2)))
                return true;
            var underlyingType1 = Nullable.GetUnderlyingType(type1);
            var underlyingType2 = Nullable.GetUnderlyingType(type2);
            if ((underlyingType1 != (Type) null) && (underlyingType2 != (Type) null))
                return underlyingType1.IsCompatibleType(underlyingType2);
            return false;
        }

        public static bool IsImplementsInterface(this Type objectType, Type interfaceType)
        {
            if (!interfaceType.IsInterface)
                throw new ArgumentException($"查找类似 指定的类型{(object) interfaceType.Name}不是接口类型。", nameof(interfaceType));
            foreach (var type in objectType.GetInterfaces())
            {
                if ((type == interfaceType) || (type.Name.Contains("`") && ((type.Name == interfaceType.Name) && (type.Assembly == interfaceType.Assembly))))
                    return true;
            }

            return false;
        }

        public static bool IsInstanceOfT<T>(this Type instanceType) => IsInstanceOfType(instanceType, typeof(T));

        public static bool IsInstanceOfT<T>(this object instance)
        {
            if (instance == null)
                return false;
            return IsInstanceOfType(instance.GetType(), typeof(T));
        }

        public static bool IsInstanceOfType(this Type instanceType, Type type)
        {
            if (instanceType == type)
                return true;
            foreach (var type1 in instanceType.GetInterfaces())
            {
                if (type1 == type)
                    return true;
            }

            for (; instanceType != Constants.ObjectType; instanceType = instanceType.BaseType)
            {
                if (type == instanceType)
                    return true;
            }

            return false;
        }

        public static bool IsInstanceOfType(this object instance, Type type)
        {
            if (instance == null)
                return false;
            return IsInstanceOfType(instance.GetType(), type);
        }

        public static bool IsNullableType(this Type type) => type.IsGenericType && (type.GetGenericTypeDefinition() == Constants.NullableType);

        public static bool IsValid(this Version value)
        {
            if (value == (Version) null)
                return false;
            return (value.Major > 0) || (value.Minor > 0) || (value.Build > 0) || (value.Revision > 0);
        }

        public static bool IsValid(this DateTime value) => (!(DateTime.MinValue == value)) && (!(DateTime.MaxValue == value));

        public static bool IsValid(this DateTime? value)
        {
            int num;
            if (value.HasValue)
            {
                var minValue = DateTime.MinValue;
                var nullable1 = value;
                if (((!nullable1.HasValue) ? 0 : ((minValue == nullable1.GetValueOrDefault()) ? 1 : 0)) == 0)
                {
                    var maxValue = DateTime.MaxValue;
                    var nullable2 = value;
                    num = (((!nullable2.HasValue) ? 0 : ((maxValue == nullable2.GetValueOrDefault()) ? 1 : 0)) == 0) ? 1 : 0;
                    goto label_4;
                }
            }

            num = 0;
            label_4:
            return num != 0;
        }

        public static T SafeValue<T>(this T? @this) where T : struct => @this ?? default(T);

        public static string ToLogString(this Exception ex)
        {
            if (ex == null)
                return string.Empty;
            var stringBuilder1 = new StringBuilder();
            stringBuilder1.Append("  >>异常消息（Message）:").AppendLine(ex.Message);
            stringBuilder1.Append("  >>异常来源（Source）:").AppendLine(ex.Source);
            stringBuilder1.Append("  >>异常类型（ExceptionType）:").AppendLine(ex.GetType().ToString());
            stringBuilder1.Append("  >>原生异常类型（BaseExceptionType）:").AppendLine(ex.GetBaseException().GetType().ToString());
            stringBuilder1.Append("  >>出错的方法签名（TargetSite）:").Append((object) ex.TargetSite);
            if (ex.Data.Count > 0)
            {
                stringBuilder1.Append(Environment.NewLine);
                stringBuilder1.Append("  >>自定义数据（Data）:");
                var stringBuilder2 = new StringBuilder();
                foreach (DictionaryEntry dictionaryEntry in ex.Data)
                    stringBuilder2.Append("Key:" + dictionaryEntry.Key + "，Value:" + dictionaryEntry.Value + "; ");
                stringBuilder1.Append((object) stringBuilder2);
            }

            if (!string.IsNullOrEmpty(ex.StackTrace))
            {
                stringBuilder1.Append(Environment.NewLine);
                stringBuilder1.Append("  >>堆栈信息（StackTrace）:");
                stringBuilder1.Append(Environment.NewLine);
                stringBuilder1.Append(ex.StackTrace);
            }

            if (ex.InnerException != null)
            {
                stringBuilder1.Append(Environment.NewLine);
                stringBuilder1.Append(">>========================== 内部异常（InnerException）==========================");
                stringBuilder1.Append(Environment.NewLine);
                stringBuilder1.Append(ex.InnerException.ToLogString());
            }

            return stringBuilder1.ToString();
        }

        public static IQueryable<T> WhereIf<T>(this IQueryable<T> @this, System.Linq.Expressions.Expression<Func<T, bool>> predicate, bool condition) => condition ? @this.Where<T>(predicate) : @this;

        public static object CopyO(this object obj)
        {
            var type = obj.GetType();
            object obj1;
            if (type.IsValueType)
            {
                obj1 = obj;
            }
            else
            {
                obj1 = Activator.CreateInstance(type);
                foreach (var member in obj.GetType().GetMembers())
                {
                    if (member.MemberType == MemberTypes.Field)
                    {
                        var fieldInfo = (FieldInfo) member;
                        var obj2 = fieldInfo.GetValue(obj);
                        if (obj2 is ICloneable)
                            fieldInfo.SetValue(obj1, (obj2 as ICloneable).Clone());
                        else
                            fieldInfo.SetValue(obj1, obj2.CopyT<object>());
                    }
                    else if (member.MemberType == MemberTypes.Property)
                    {
                        var propertyInfo = (PropertyInfo) member;
                        if (propertyInfo.GetSetMethod(false) != (MethodInfo) null)
                        {
                            var obj2 = propertyInfo.GetValue(obj, (object[]) null);
                            if (obj2 is ICloneable)
                                propertyInfo.SetValue(obj1, (obj2 as ICloneable).Clone(), (object[]) null);
                            else
                                propertyInfo.SetValue(obj1, obj2.CopyT<object>(), (object[]) null);
                        }
                    }
                }
            }

            return obj1;
        }

        public static string GetDescription(FieldInfo field)
        {
            var customAttribute = Attribute.GetCustomAttribute((MemberInfo) field, typeof(DescriptionAttribute), false);
            return (customAttribute == null) ? string.Empty : ((DescriptionAttribute) customAttribute).Description;
        }
    }

    public static class Constants
    {
        public static readonly Type DateTimeType = typeof(DateTime);
        public static readonly Type DoubleType = typeof(double);
        public static readonly object[] EmptyObjectArray = new object[0];
        public static readonly string[] EmptyStringArrray = new string[0];
        public static readonly Type EnumType = typeof(Enum);
        public static readonly Type NullableDateTimeType = typeof(DateTime?);
        public static readonly Type NullableType = typeof(Nullable<>);
        public static readonly Type NullGenericListType = typeof(List<>);
        public static readonly Type ObjectType = typeof(object);
        public static readonly Type StringType = typeof(string);
        public static readonly Type VoidType = typeof(void);
    }
}